#include <config.h>
#include <display_manager.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>

static int g_iFdFB;
static struct fb_var_screeninfo g_tFBVar;
static struct fb_fix_screeninfo g_tFBFix;
static unsigned char *g_pucFbBuffer;
static int g_iScreenSize;
static int g_iLineSize;
static int g_iPixelSize;

static T_DisplayOpr g_tFBDisplayOpr;


static int FBDeviceInit(void)
{
	int iRet;

	g_iFdFB = open(FB_DEVICE_NAME, O_RDWR);
	if(0 > g_iFdFB)
	{
		DBG_PRINTF("Can't open Fb Device\n");
		return -1;
	}

	iRet = ioctl(g_iFdFB, FBIOGET_VSCREENINFO, &g_tFBVar);
	if(0 > iRet)
	{
		DBG_PRINTF("Can't get var params\n");
		return -1;
	}

	iRet = ioctl(g_iFdFB, FBIOGET_FSCREENINFO, &g_tFBFix);
	if(0 > iRet)
	{
		DBG_PRINTF("Can't get var params\n");
		return -1;
	}

	g_tFBDisplayOpr.iXres = g_tFBVar.xres;
	g_tFBDisplayOpr.iYres = g_tFBVar.yres;
	g_tFBDisplayOpr.iBpp  = g_tFBVar.bits_per_pixel;

	g_iScreenSize = g_tFBVar.xres * g_tFBVar.yres * g_tFBVar.bits_per_pixel/8;
	g_iLineSize   = g_tFBVar.xres * g_tFBVar.bits_per_pixel/8;
	g_iPixelSize  = g_tFBVar.bits_per_pixel/8;
	g_pucFbBuffer = (unsigned char *)mmap(NULL, g_iScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, g_iFdFB, 0);
	if((unsigned char *)-1 == g_pucFbBuffer)
	{
		DBG_PRINTF("Can't mmap fb buffer\n");
		return -1;
	}
	return 0;
}

/* 0xRRGGBB */
static int FBShowPixel(int iPenX, int iPenY, unsigned int dwColor)
{
	unsigned char  *pucFb = g_pucFbBuffer + iPenX*g_iPixelSize + iPenY*g_iLineSize;
	unsigned short *pucFb16;
	unsigned int   *pucFb32;
	int iRed, iGreen, iBlue;

	pucFb16 = (unsigned short *)pucFb;
	pucFb32 = (unsigned int *)pucFb;

	switch(g_tFBVar.bits_per_pixel)
	{
		case 8:
		{
			*pucFb = dwColor;
			break;
		}

		case 16:
		{
			iRed = (dwColor>>(16+3)) & 0x1f;
			iGreen = (dwColor>>(8+2)) & 0x3f;
			iBlue = (dwColor>>3) & 0x1f;
			dwColor = (iRed<<11) | (iGreen<<5) | (iBlue);
			*pucFb16 = dwColor;
			break;
		}

		case 32:
		{
			*pucFb32 = dwColor;
			break;
		}
	}
	return 0;
}

static int FBClearScreen(unsigned int dwColor)
{
	unsigned char  *pucFb = g_pucFbBuffer;
	unsigned short *pucFb16;
	unsigned int   *pucFb32;
	int iRed, iGreen, iBlue;
	int i = 0;

	pucFb16 = (unsigned short *)pucFb;
	pucFb32 = (unsigned int *)pucFb;

	switch(g_tFBVar.bits_per_pixel)
	{
		case 8:
		{
			memset(pucFb, dwColor, g_iScreenSize);
			break;
		}

		case 16:
		{
			iRed = (dwColor>>(16+3)) & 0x1f;
			iGreen = (dwColor>>(8+2)) & 0x3f;
			iBlue = (dwColor>>3) & 0x1f;
			dwColor = (iRed<<11) | (iGreen<<5) | (iBlue);
			while(i < g_iScreenSize)
			{
				*pucFb16 = dwColor;
				pucFb16++;
				i += 2;
			}
			break;
		}

		case 32:
		{
			while(i < g_iScreenSize)
			{
				*pucFb32 = dwColor;
				pucFb32++;
				i += 4;
			}
			break;
		}
	}
	return 0;

}

static T_DisplayOpr g_tFBDisplayOpr = {
	.name        = "fb",
	.DeviceInit  = FBDeviceInit,
	.ShowPixel   = FBShowPixel,
	.ClearScreen = FBClearScreen,
};

int FBRegister(void)
{
	return RegisterDisplayOpr(&g_tFBDisplayOpr);
}

